import os
from typing import List

from openai import OpenAI


class OpenRouterService:
    """Service for interacting with OpenRouter API to enhance content."""

    def __init__(self):
        """Initialize the OpenRouter service.

        Raises:
            ValueError: If OPENROUTER_API_KEY environment variable is not set
        """
        api_key = os.getenv("OPENROUTER_API_KEY")
        if not api_key:
            raise ValueError("OPENROUTER_API_KEY environment variable is not set")

        self.client = OpenAI(
            base_url="https://openrouter.ai/api/v1",
            api_key=api_key,
            default_headers={
                "HTTP-Referer": "https://github.com/your-username/markdown-to-wechat",  # Optional
                "X-Title": "Markdown to WeChat Converter",  # Optional
            },
        )

    def summarize_for_title(self, body_text: str) -> str:
        """Generate a title from the article content.

        Args:
            body_text: The full article content including front matter

        Returns:
            A generated title that highlights key points and attracts readers
        """
        clean_content, clean_lines = self.cleanup_text(body_text, 5)

        ret = ""
        try:
            response = self.client.chat.completions.create(
                model="deepseek/deepseek-v3-base:free",
                messages=[
                    {
                        "role": "system",
                        "content": "你是一个标题生成器。只生成标题，不要其他文本。标题必须是中文，不超过100个字符，并描述主要主题。不要包含任何markdown、引号或额外的格式。",
                    },
                    {"role": "user", "content": clean_content},
                ],
                temperature=0.3,  # Lower temperature for more focused output
                max_tokens=20,  # Further limit response length
                top_p=0.8,  # More focused token selection
                frequency_penalty=0.0,  # No need for frequency penalty in short titles
            )

            if (
                response
                and response.choices
                and response.choices[0].message
                and response.choices[0].message.content
            ):
                ret = response.choices[0].message.content.strip()
            else:
                # Use the first non-empty line as title if API fails
                ret = next((line for line in clean_lines if line), "默认标题")
        except Exception:
            # Use the first non-empty line as title if API fails
            ret = next((line for line in clean_lines if line), "默认标题")

        # Clean up the title
        title = (
            ret.replace("#", "")
            .replace("`", "")
            .replace('"', "")
            .replace("'", "")
            .replace("\n", " ")  # Replace newlines with spaces
            .strip()
        )

        # If title is still too long, truncate it
        if len(title) > 100:
            title = title[:97] + "..."

        return title

    def summarize_for_subtitle(self, body_text: str) -> str:
        """Generate a subtitle/description from the article content.

        Args:
            body_text: The full article content including front matter

        Returns:
            A concise description of the article content in one sentence (max 50 characters)
        """
        # Clean up the content
        clean_content, clean_lines = self.cleanup_text(body_text, 5)

        try:
            response = self.client.chat.completions.create(
                model="deepseek/deepseek-v3-base:free",
                messages=[
                    {
                        "role": "system",
                        "content": "你是一个资深自媒体运营者。请生成一个单句描述（最多50个字符），捕捉文章的精髓。描述必须是中文，以句号结尾，不应包含任何markdown、引号或额外的格式。",
                    },
                    {"role": "user", "content": clean_content},
                ],
                temperature=0.3,  # Lower temperature for more focused output
                max_tokens=15,  # Limit response length for shorter description
                top_p=0.8,  # More focused token selection
                frequency_penalty=0.0,  # No need for frequency penalty in short description
            )

            if (
                response
                and response.choices
                and response.choices[0].message
                and response.choices[0].message.content
            ):
                subtitle = response.choices[0].message.content.strip()
            else:
                # Use a default subtitle if API fails
                subtitle = "文章简介。"
        except Exception:
            # Use a default subtitle if API fails
            subtitle = "文章简介。"

        # Clean up the subtitle
        subtitle = (
            subtitle.replace("#", "")
            .replace("`", "")
            .replace('"', "")
            .replace("'", "")
            .replace("\n", " ")  # Replace newlines with spaces
            .strip()
        )

        # Remove any existing periods or ellipsis
        subtitle = subtitle.rstrip("。.…")

        # Process the subtitle
        if len(subtitle) > 46:
            # For long subtitles, truncate and add ellipsis
            # Remove any trailing punctuation
            subtitle = subtitle[:46].rstrip(",.。!?！？、，")
            return subtitle + "..."
        else:
            # For short subtitles, add period
            return subtitle + "。"

    def generate_tags(self, content: str) -> List[str]:
        # Extract content without front matter
        clean_content, clean_lines = self.cleanup_text(content, 5)
        prompt = (
            "你是一个标签生成器。根据下面的文章内容，生成恰好三个最能代表文章主题的标签。"
            "每个标签必须：\n"
            "1. 使用中文词语或英文专有名词（例如：人工智能、Python、Docker）\n"
            "2. 不包含空格或特殊字符\n"
            "3. 与内容相关\n"
            "4. 简洁明了\n"
            "5. 不能重复\n\n"
            "6. 不要包含连字符，即：'-'和'——'\n\n"
            "内容：\n"
            f"{clean_content}\n\n"
            "只回复三个不同的标签，每行一个，不要标点或解释。每个标签必须是中文词语或英文专有名词。"
        )

        try:
            response = self._get_response_with_retry(prompt)
            if response:
                tags = [tag.strip() for tag in response.split("\n") if tag.strip()][:3]
            else:
                tags = []
        except Exception:
            tags = []

        # Clean up tags
        cleaned_tags = []
        for tag in tags:
            # Remove any special characters except Chinese characters, alphanumeric characters, and hyphens
            cleaned = "".join(
                c for c in tag if c.isalnum() or c == "-" or "\u4e00" <= c <= "\u9fff"
            )
            if cleaned:
                cleaned_tags.append(cleaned)

        # If we don't have enough tags, add generic ones
        while len(cleaned_tags) < 3:
            cleaned_tags.append(f"tag{len(cleaned_tags)+1}")

        return cleaned_tags[:3]  # Ensure we return exactly 3 tags

    def suggest_category(
        self, content: str, existing_categories: List[str] = None
    ) -> str:
        """
        Suggest a category for the article content.

        Args:
            content: The article content to analyze
            existing_categories: Optional list of existing categories to choose from

        Returns:
            A suggested category name
        """
        # Define predefined categories
        predefined = [
            "个人观点",
            "实用总结",
            "方法论",
            "AI编程",
            "软件工程",
            "工程效率",
            "人工智能",
        ]

        # Extract content without front matter
        clean_content, clean_lines = self.cleanup_text(content, 5)

        # If we have maximum categories, only use existing ones
        if existing_categories and len(existing_categories) >= 10:
            try:
                response = self._get_response_with_retry(
                    "你是一个内容分类器。根据下面的文章内容，从以下列表中选择一个最合适的分类：\n"
                    f"{', '.join(existing_categories)}\n\n"
                    "内容：\n"
                    f"{clean_content}\n\n"
                    "只回复分类名称，不要解释。"
                )
                category = response.strip()
                # If no valid category is returned, use the most appropriate existing one
                if not category or category not in existing_categories:
                    # For Python/programming content, prefer software engineering related categories
                    if any(
                        word in clean_content.lower()
                        for word in ["python", "programming", "code", "software"]
                    ):
                        for preferred in ["软件工程", "AI编程", "工程效率"]:
                            if preferred in existing_categories:
                                return preferred
                    # Default to the first category if no better match
                    return existing_categories[0]
                return category
            except Exception:
                # Default to the first category if API fails
                return existing_categories[0]

        # Otherwise, try to use predefined categories first
        try:
            response = self._get_response_with_retry(
                "你是一个内容分类器。根据下面的文章内容，从以下列表中选择一个最合适的分类：\n"
                f"{', '.join(predefined)}\n\n"
                "内容：\n"
                f"{clean_content}\n\n"
                "如果没有合适的分类，建议一个新的分类名称（最多3个词）。只回复分类名称，"
                "不要解释或标点。"
            )
            category = (
                response.strip() if response else "个人观点"
            )  # Default category if API fails
        except Exception:
            category = "个人观点"  # Default category if API fails

        # Validate the category
        if not category:
            return "个人观点"  # Default category

        # Clean up the category
        category = (
            category.replace("#", "")
            .replace("`", "")
            .replace('"', "")
            .replace("'", "")
            .replace("\n", " ")  # Replace newlines with spaces
            .strip()
        )

        # If category is not in predefined list, ensure it's valid
        if category not in predefined:
            # Ensure it's not too long
            words = category.split()
            if len(words) > 3:
                category = " ".join(words[:3])

            # Ensure it only contains valid characters
            category = "".join(
                c
                for c in category
                if c.isalnum() or c.isspace() or "\u4e00" <= c <= "\u9fff"
            )

        return category.strip()

    def generate_seo_keywords(self, body_text: str) -> List[str]:
        clean_content, clean_lines = self.cleanup_text(body_text, 5)
        # Handle empty content
        if not clean_content:
            return []

        response = self.client.chat.completions.create(
            model="deepseek/deepseek-v3-base:free",
            messages=[
                {
                    "role": "system",
                    "content": "为给定的文章内容生成SEO关键词。返回最多20个相关的关键词或关键短语。每个关键词/短语应该是1-3个词长。只返回逗号分隔的关键词。关注对搜索引擎优化有价值的中文术语。",
                },
                {"role": "user", "content": clean_content},
            ],
            temperature=0.3,  # Lower temperature for more focused output
            max_tokens=100,  # Keywords can be longer than titles/tags
            top_p=0.8,  # More focused token selection
        )

        # Handle empty or invalid response
        if (
            not response
            or not response.choices
            or not response.choices[0].message
            or not response.choices[0].message.content
        ):
            return ["default-keyword"]  # Return a default keyword if API fails

        keywords_text = response.choices[0].message.content.strip()

        # Split the comma-separated keywords and clean them
        raw_keywords = [kw.strip() for kw in keywords_text.split(",")]

        # Clean and format each keyword
        keywords = []
        for keyword in raw_keywords:
            # Remove any quotes or special characters
            clean_keyword = (
                keyword.replace('"', "")
                .replace("'", "")
                .replace("[", "")
                .replace("]", "")
                .strip()
            )

            # Skip empty keywords
            if not clean_keyword:
                continue

            # Ensure keyword is not too long (max 3 words)
            words = clean_keyword.split()
            if len(words) > 3:
                clean_keyword = " ".join(words[:3])

            # Add keyword if it's unique
            if clean_keyword and clean_keyword not in keywords:
                keywords.append(clean_keyword)

            # Stop if we have 20 keywords
            if len(keywords) >= 20:
                break

        return keywords

    def _get_response_with_retry(self, prompt: str, max_retries: int = 3) -> str:
        for attempt in range(max_retries):
            try:
                response = self.client.chat.completions.create(
                    model="deepseek/deepseek-v3-base:free",
                    messages=[{"role": "user", "content": prompt}],
                    temperature=0.3,  # Lower temperature for more focused output
                    max_tokens=50,  # Keep responses concise
                    top_p=0.8,  # More focused token selection
                )
                if (
                    response
                    and response.choices
                    and response.choices[0].message
                    and response.choices[0].message.content
                ):
                    return response.choices[0].message.content.strip()
            except Exception as e:
                if attempt == max_retries - 1:  # Last attempt
                    raise RuntimeError(
                        f"Failed to get response after {max_retries} attempts: {str(e)}"
                    ) from e
                continue  # Try again
        return ""  # Return empty string if all attempts fail

    def cleanup_text(
        self, body_text: str, first_lines: int = 10
    ) -> tuple[str, list[str]]:
        content_lines = body_text.strip().split("\n")
        clean_lines = []
        for line in content_lines:
            line = line.strip()
            if not line:
                continue
            clean_lines.append(line)
        return " ".join(clean_lines[:first_lines]), clean_lines
